package com.ruoyi.project.system.controller;

import java.io.IOException;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.UUID;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.lang.Assert;
import cn.hutool.core.util.NumberUtil;
import cn.hutool.core.util.ObjectUtil;
import cn.hutool.core.util.StrUtil;
import com.alibaba.fastjson.JSONObject;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.ruoyi.common.exception.CustomException;
import com.ruoyi.common.exception.user.UserPasswordNotMatchException;
import com.ruoyi.common.utils.MessageUtils;
import com.ruoyi.framework.dinktalk.DingTalkUserInfo;
import com.ruoyi.framework.manager.AsyncManager;
import com.ruoyi.framework.manager.factory.AsyncFactory;
import com.ruoyi.framework.security.service.UserDetailsServiceImpl;
import com.ruoyi.framework.weChat.social.BaseConnectionData;
import com.ruoyi.framework.weChat.social.SocialRedisHelper;
import com.ruoyi.framework.weChat.social.SocialUserInfo;
import com.ruoyi.project.system.domain.SysAppKey;
import com.ruoyi.project.system.domain.SysSocial;
import com.ruoyi.project.system.domain.vo.ResetUserPwdInfo;
import com.ruoyi.project.system.service.ISysAppKeyService;
import com.ruoyi.project.system.service.ISysSocialService;
import com.ruoyi.project.system.service.ISysUserService;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import me.zhyd.oauth.config.AuthConfig;
import me.zhyd.oauth.exception.AuthException;
import me.zhyd.oauth.model.AuthCallback;
import me.zhyd.oauth.model.AuthResponse;
import me.zhyd.oauth.request.*;
import me.zhyd.oauth.utils.AuthStateUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.authentication.BadCredentialsException;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.social.connect.Connection;
import org.springframework.social.connect.ConnectionData;
import org.springframework.social.connect.web.ProviderSignInUtils;
import org.springframework.social.security.SocialUserDetails;
import org.springframework.web.bind.annotation.*;
import com.ruoyi.common.constant.Constants;
import com.ruoyi.common.utils.ServletUtils;
import com.ruoyi.framework.security.LoginUser;
import com.ruoyi.framework.security.service.SysLoginService;
import com.ruoyi.framework.security.service.SysPermissionService;
import com.ruoyi.framework.security.service.TokenService;
import com.ruoyi.framework.web.domain.AjaxResult;
import com.ruoyi.project.system.domain.SysMenu;
import com.ruoyi.project.system.domain.SysUser;
import com.ruoyi.project.system.service.ISysMenuService;
import org.springframework.web.context.request.ServletWebRequest;

import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

/**
 * 登录验证
 *
 * @author ruoyi
 */
@Api(tags = "用户登录")
@RestController
public class SysLoginController {
    @Autowired
    private SysLoginService loginService;

    @Autowired
    private ISysMenuService menuService;

    @Autowired
    private SysPermissionService permissionService;

    @Autowired
    private TokenService tokenService;

    @Autowired
    private ProviderSignInUtils providerSignInUtils;

    @Autowired
    private SocialRedisHelper socialRedisHelper;

    @Autowired
    private ISysUserService userService;

    @Value("${baseframe.url.address}")
    private String url;

    @Autowired
    private ISysSocialService sysSocialService;

    @Resource
    private AuthenticationManager authenticationManager;

    @Autowired
    private UserDetailsServiceImpl userDetailsService;

    @Autowired
    private ISysAppKeyService appKeyService;

    /**
     * 登录方法
     *
     * @param username 用户名
     * @param password 密码
     * @param
     * @param uuid     唯一标识
     * @return 结果
     */
    @PostMapping("/login")
    public AjaxResult login(String username, String password, String code, String uuid, HttpServletRequest request) {

        AjaxResult ajax = AjaxResult.success();
        String token = request.getParameter("token");
        if (StrUtil.isNotEmpty(token)) {
            return ajax;
        }
        // 生成令牌
        token = loginService.login(username, password, code, uuid);
        ajax.put(Constants.TOKEN, token);
        return ajax;
    }

    @PostMapping(value = "/mobile/login")
    public AjaxResult mobileLogin(HttpServletRequest request,HttpServletResponse response) throws IOException {
        String phone = request.getParameter("phone");
        AjaxResult ajax = new AjaxResult();
        // 生成令牌
        String token = loginService.mobileLogin(phone);
        ajax.put(Constants.TOKEN,token);
        ajax.put("code",200);
        return ajax;

    }

    @PostMapping(value = "/forgetPwd")
    @ApiOperation("忘记密码并修改密码")
    public AjaxResult forgetPwd(@RequestBody ResetUserPwdInfo userPwdInfo)  {

        Assert.notBlank(userPwdInfo.getNewPwd(),"新密码不能为空");
        Assert.notBlank(userPwdInfo.getSureNewPwd(),"请再次输入新密码");
        Assert.notBlank(userPwdInfo.getSmsCode(),"验证码不能为空");

        if (StrUtil.isEmpty(userPwdInfo.getPhonenumber()) && StrUtil.isEmpty(userPwdInfo.getEmail())) {
            throw new CustomException("手机号码或邮箱账号不能为空");
        }

        int resInt=0;
        //验证手机号验证码是否争取
        boolean validate = loginService.validate(userPwdInfo);
        // 如果手机号验证码验证通过，则更新用户登录密码
        if (validate) {
           resInt = userService.resetUserPwd(userPwdInfo);
        }
        return AjaxResult.success("密码修改成功");
    }

    /**
     * 保存完信息然后跳转到绑定用户信息页面
     *
     * @param request
     * @param response
     * @throws IOException
     */
    @GetMapping("/socialSignUp")
    public void socialSignUp(HttpServletRequest request, HttpServletResponse response) throws IOException {
        String uuid = UUID.randomUUID().toString();
        SocialUserInfo userInfo = new SocialUserInfo();
        Connection<?> connectionFromSession = providerSignInUtils.getConnectionFromSession(new ServletWebRequest(request));
        userInfo.setHeadImg(connectionFromSession.getImageUrl());
        userInfo.setNickname(connectionFromSession.getDisplayName());
        userInfo.setProviderId(connectionFromSession.getKey().getProviderId());
        userInfo.setProviderUserId(connectionFromSession.getKey().getProviderUserId());
        ConnectionData data = connectionFromSession.createData();
        BaseConnectionData preConnectionData = new BaseConnectionData();
        BeanUtil.copyProperties(data, preConnectionData);
        socialRedisHelper.saveConnectionData(uuid, preConnectionData);
        // 跳转到用户绑定页面
        response.sendRedirect(url + "/bind?key=" + uuid);
    }

    /**
     * 社交登录绑定
     *
     * @param user
     * @return
     */
    @PostMapping("/bind1")
    public AjaxResult register(@RequestBody SysUser user) {
        return AjaxResult.success(userService.doPostSignUp(user));
    }

    /**
     * 社交登录绑定
     *
     * @param user
     * @return
     */
    @PostMapping("/bind")
    public AjaxResult register1(@RequestBody SysUser user) {
        AjaxResult ajax = new AjaxResult();
        SysUser sysuser = userService.selectUserByUserName(user.getUserName());
        if ("1".equals(sysuser.getStatus())) {
            return new AjaxResult(2002,"该账号已被禁用");
        }
        String token = userService.doPostSignUp1(user);
        if (StrUtil.isNotEmpty(token)) {
            ajax.put(Constants.TOKEN, token);
            ajax.put("code", 200);
            return ajax;
        } else {
            return AjaxResult.error();
        }


    }


    /**
     * 获取用户信息
     *
     * @return 用户信息
     */
    @GetMapping("getInfo")
    public AjaxResult getInfo() {
        LoginUser loginUser = tokenService.getLoginUser(ServletUtils.getRequest());
        SysUser user = loginUser.getUser();
        // 角色集合
        Set<String> roles = permissionService.getRolePermission(user);
        // 权限集合
        Set<String> permissions = permissionService.getMenuPermission(user);
        AjaxResult ajax = AjaxResult.success();
        ajax.put("user", user);
        ajax.put("roles", roles);
        ajax.put("permissions", permissions);
        return ajax;
    }

    /**
     * 获取路由信息
     *
     * @return 路由信息
     */
    @GetMapping("getRouters")
    public AjaxResult getRouters() {
        LoginUser loginUser = tokenService.getLoginUser(ServletUtils.getRequest());
        // 用户信息
        SysUser user = loginUser.getUser();
        List<SysMenu> menus = menuService.selectMenuTreeByUserId(user.getUserId());
        return AjaxResult.success(menuService.buildMenus(menus));
    }

    // 第三方登录，参数source用于标定是哪个社交软件，如dingtalk为钉钉，wechat则为微信
    @GetMapping("/render/{source}")
    @ApiOperation(value = "进入render", notes = "进入render")
    public void renderAuth(@PathVariable("source") String source, HttpServletResponse response) throws IOException {
        System.out.println("进入render：" + source);
        AuthRequest authRequest = getAuthRequest(source);
        String authorizeUrl = authRequest.authorize(AuthStateUtils.createState());
        System.out.println(authorizeUrl);
        response.sendRedirect(authorizeUrl);
    }

    /**
     * oauth平台中配置的授权回调地址，以本项目为例，回调地址应为：http://huangxiang.utools.club:8089/callback/dingtalk
     */
    @GetMapping("/callback/{source}")
    @ApiOperation(value = "进入callback", notes = "进入callback")
    public AjaxResult login(@PathVariable("source") String source, AuthCallback callback, HttpServletResponse response) {
        System.out.println("进入callback：" + source + " callback params：" + JSONObject.toJSONString(callback));
        AuthRequest authRequest = getAuthRequest(source);
        AuthResponse response1 = authRequest.login(callback);
        //将返回的authResponse的数据转换为BaseConnectionData
        String uuid = UUID.randomUUID().toString();
        if (2000 == response1.getCode()) {
            ObjectMapper objectMapper = new ObjectMapper();
            try {
                DingTalkUserInfo dingTalkUserInfo = objectMapper.readValue(JSONObject.toJSONString(response1.getData()), DingTalkUserInfo.class);
                System.out.println("获取的用户信息名：" + dingTalkUserInfo.getUsername() + "****************");
                BaseConnectionData preConnectionData = new BaseConnectionData();
                preConnectionData.setProviderId(source);
                preConnectionData.setProviderUserId(dingTalkUserInfo.getUuid());
                preConnectionData.setDisplayName(dingTalkUserInfo.getUsername());
                preConnectionData.setImageUrl(dingTalkUserInfo.getAvatar());
                preConnectionData.setAccessToken(dingTalkUserInfo.getToken().getAccessToken());
                preConnectionData.setRefreshToken(dingTalkUserInfo.getToken().getRefreshToken());
                preConnectionData.setSecret(dingTalkUserInfo.getToken().getOpenId());
                socialRedisHelper.saveConnectionData(uuid, preConnectionData);

                LambdaQueryWrapper<SysSocial> wrapper = Wrappers.lambdaQuery();
                if (StrUtil.isNotEmpty(dingTalkUserInfo.getUuid())) {
                    wrapper.eq(SysSocial::getProviderUserId, dingTalkUserInfo.getUuid());
                    wrapper.eq(SysSocial::getProviderId, source);
                }
                SysSocial social = sysSocialService.getOne(wrapper);
                if (ObjectUtil.isNull(social)) {
                    // 跳转到用户绑定页面
                    response.sendRedirect(url + "/bind?key=" + uuid);
                } else {
                    SysUser sysUser = new SysUser();
                    sysUser.setUserId(Long.parseLong(social.getUserId()));
                    SysUser securityUser = userService.findSecurityUserByUser(sysUser);
                    if (ObjectUtil.isNull(securityUser)) {
                        sysSocialService.remove(new LambdaQueryWrapper<SysSocial>().eq(SysSocial::getUserId, social.getUserId())
                                .eq(SysSocial::getProviderUserId, social.getProviderUserId())
                                .eq(SysSocial::getProviderId, source));
                        response.sendRedirect(url + "/bind?key=" + uuid);
                    }
                    //userDetailsService.loadUserByUserId(String.valueOf(securityUser.getUserId()));
                    //生成token
                    LoginUser loginUser = (LoginUser) userDetailsService.loadUserByUsername(securityUser.getUserName());
                    String token = tokenService.createToken(loginUser);
                    response.sendRedirect(url + "/login?token=" + token);
                }

            } catch (Exception e) {
                throw new CustomException("读取用户信息失败" + e.getMessage());
            }

        } else {
            throw new CustomException("钉钉获取用户信息失败");
        }
        System.out.println(JSONObject.toJSONString(response1));
        return AjaxResult.success();
    }


    /**
     * 封装根据具体的授权来源，获取授权请求工具类
     *
     * @param source
     * @return
     */
    private AuthRequest getAuthRequest(String source) {
        AuthRequest authRequest = null;
        SysAppKey appkey = getAppkey(source);
        if (ObjectUtil.isNull(appkey)) {
            throw new CustomException("该社交账号的密钥不存在");
        }
        switch (source) {
            case "dingtalk":
                authRequest = new AuthDingTalkRequest(AuthConfig.builder()
//                        .clientId("dingoaxvbbayguvp5l3hyt")
                        .clientId(appkey.getAppKey())
//                        .clientSecret("MB1S__fw58iEPyv_p357V1xMh3ZiFLVTKlIu1y4ZPRVjasHEhIyx4NspIHTdE02P")
                        .clientSecret(appkey.getAppSecret())
                        .redirectUri("http://huangxiang.utools.club:8089/callback/dingtalk")
                        .build());
                break;
            case "wechat":
                authRequest = new AuthWeChatRequest(AuthConfig.builder()
                        .clientId("wx714311100f2a5bd2")
                        .clientSecret("7d806191c293a5d375d54ea7571b82fa")
                        .redirectUri("http://huangxiang.utools.club:8089/callback/wechat")
                        .build());
                break;
            case "douyin":
                authRequest = new AuthDouyinRequest(AuthConfig.builder()
                        .clientId("")
                        .clientSecret("")
                        .redirectUri("http://127.0.0.1:8080/oauth/callback/douyin")
                        .build());
                break;
            default:
                break;
        }
        if (null == authRequest) {
            throw new AuthException("未获取到有效的Auth配置");
        }
        return authRequest;
    }

    public SysAppKey getAppkey(String source) {
        SysAppKey appKey=null;
        SysAppKey sysAppKey = appKeyService.getOne(new LambdaQueryWrapper<SysAppKey>().eq(SysAppKey::getAppType, source));
        if (ObjectUtil.isNotNull(sysAppKey)) {
            appKey=sysAppKey;
        }
        return appKey;
    }
}
